package com.repair.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.repair.api.user.constant.UserOperateType;
import com.repair.api.user.constant.UserStateEnum;
import com.repair.api.user.request.UserBlockRequest;
import com.repair.api.user.request.UserQueryPageRequest;
import com.repair.api.user.request.UserRegisterRequest;
import com.repair.api.user.request.UserUpdateStatusRequest;
import com.repair.api.user.request.condition.UserIdQueryCondition;
import com.repair.api.user.request.param.UserQueryParam;
import com.repair.api.user.request.param.UserUpdateStatusParam;
import com.repair.api.user.response.UserOperateResponse;
import com.repair.api.user.response.UserQueryResponse;
import com.repair.api.user.response.data.UserInfo;
import com.repair.base.exception.BizException;
import com.repair.base.exception.SystemErrorCode;
import com.repair.base.exception.SystemException;
import com.repair.base.response.NoResultResponse;
import com.repair.base.response.PageResponse;
import com.repair.base.utils.ResponseCode;
import com.repair.file.exception.OssErrorCode;
import com.repair.file.exception.OssException;
import com.repair.file.service.OssService;
import com.repair.lock.annotaion.DistributeLock;
import com.repair.satoken.utils.StpKit;
import com.repair.user.exception.UserServiceErrorCode;
import com.repair.user.exception.UserServiceException;
import com.repair.user.mapper.UserMapper;
import com.repair.user.pojo.convertor.UserConvertor;
import com.repair.user.pojo.dto.UserUpdateParam;
import com.repair.user.pojo.entity.User;
import com.repair.user.pojo.entity.convetor.UserConverter;
import com.repair.user.service.UserOperateStreamService;
import com.repair.user.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.repair.api.user.constant.NicknamePrefix.NICKNAME_PREFIX;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 郑春雨
 * @since 2024-09-12
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, InitializingBean {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserOperateStreamService userOperateStreamService;

    @Autowired
    private UserServiceImpl userServiceImpl;

    private RBloomFilter<String> nickNameBloomFilter;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private OssService ossService;

    @Override
    public User selectByTelephone(String telephone) {
        return userMapper.selectOne(new QueryWrapper<User>().eq("telephone", telephone));
    }

    @DistributeLock(scene = "register_user", keyExpression = "#userRegisterRequest.telephone", expireTime = 5000)
    @Override
    public UserOperateResponse register(UserRegisterRequest userRegisterRequest) {
        // 幂等
        User userExists = userMapper.selectOne(new QueryWrapper<User>().eq("telephone", userRegisterRequest.getTelephone()));
        if (userExists != null) {
            throw new UserServiceException(UserServiceErrorCode.USER_EXISTS);
        }
        // 注册
        User user = new User();
        String telephone = userRegisterRequest.getTelephone();
        user.setTelephone(telephone);
        // 生成昵称
        StringBuilder nickname;
        StringBuilder nickPrefix = new StringBuilder(NICKNAME_PREFIX);
        do {
            String nickMiddle = RandomUtil.randomNumbers(4);
            String nickTail = String.valueOf(System.currentTimeMillis()).substring(4, 8);
            nickname = nickPrefix.append(nickMiddle).append(nickTail);
        } while (StringUtils.isEmpty(nickname) || nickNameExist(nickname.toString()));
        addNickNameToBloomFilter(nickname.toString());
        user.setNickName(nickname.toString());
        user.setState(UserStateEnum.INIT.name());
        user.setProfilePhotoUrl("https://home-repair-system.oss-cn-beijing.aliyuncs.com/111.ico");
        user.setAuthenticationStatus((byte) 0);
        user.setLevel("3");
        user.setOrderNum(0L);
        user.setTotalOrderAmount(new BigDecimal("0.00"));
        int i = baseMapper.insert(user);
        if (i == 0) {
            throw new SystemException(SystemErrorCode.INSERT_FAILED);
        }
        // 流水
        long streamResult = userOperateStreamService.insertStream(user, UserOperateType.REGISTER);
        if (streamResult == 0) {
            throw new SystemException(SystemErrorCode.INSERT_FAILED);
        }
        UserOperateResponse userOperateResponse = new UserOperateResponse();
        userOperateResponse.setSuccess(true);
        // 单纯注册
        return userOperateResponse;
    }

    private void addNickNameToBloomFilter(String nickname) {
        nickNameBloomFilter.add(nickname);
    }

    @Override
    public UserInfo getUserInfo(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new SystemException(SystemErrorCode.SELECT_FAILED);
        }
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(user, userInfo);
        return userInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateNickName(UserUpdateParam userUpdateParam) {
        Long userId = Long.valueOf((String) StpKit.USER.getLoginId());
        User user = new User();
        BeanUtils.copyProperties(userUpdateParam, user);
        user.setId(userId);
        User userExists = userMapper.selectById(user.getId());
        if (userExists == null) {
            throw new UserServiceException(UserServiceErrorCode.USER_NOT_EXISTS);
        }
        if (StringUtils.isEmpty(userUpdateParam.getNickName()) || nickNameExist(userUpdateParam.getNickName())) {
            throw new UserServiceException(UserServiceErrorCode.USER_NICKNAME_EXISTS);
        }
        int i = userMapper.updateById(user);
        if (i == 0) {
            throw new SystemException(SystemErrorCode.UPDATE_FAILED);
        }
        long l = userOperateStreamService.insertStream(user, UserOperateType.UPDATE_INFO);
        if (l == 0) {
            throw new SystemException(SystemErrorCode.INSERT_FAILED);
        }
        // 添加到布隆过滤器
        addNickNameToBloomFilter(userUpdateParam.getNickName());
        // 刷新session
        refreshUserInSession(userId);
    }

    @Override
    public PageResponse<UserInfo> getAllUsers(UserQueryPageRequest<UserQueryParam> queryParam) {

        // 这里动态查询真尼玛麻烦 后面改成xml
        int pageSize = queryParam.getPageSize();
        int currentPage = queryParam.getCurrentPage();
        UserQueryParam queryParamData = queryParam.getData();
        String nickName = queryParamData.getNickName();
        String telephone = queryParamData.getTelephone();
        String level = queryParamData.getLevel();
        LocalDateTime createTimeMin = queryParamData.getCreateTimeMin();
        Long orderNum = queryParamData.getOrderNum();
        Long orderNumMin = queryParamData.getOrderNumMin();
        Long orderNumMax = queryParamData.getOrderNumMax();
        BigDecimal totalOrderAmountMax = queryParamData.getTotalOrderAmountMax();
        BigDecimal totalOrderAmountMin = queryParamData.getTotalOrderAmountMin();
        LocalDateTime createTimeMax = queryParamData.getCreateTimeMax();
        Long id = queryParamData.getId();

        Page<User> userPage = new Page<>(currentPage, pageSize);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("create_time");
        queryWrapper.eq(!StringUtils.isEmpty(queryParamData.getState()), "state", queryParamData.getState())
                .like(!StringUtils.isEmpty(nickName), "nick_name", nickName)
                .like(!StringUtils.isEmpty(telephone), "telephone", telephone)
                .like(id != null, "id", id)
                .eq(!StringUtils.isEmpty(level), "level", level)
                .gt(createTimeMin != null, "create_time", createTimeMin)
                .lt(createTimeMax != null, "create_time", createTimeMax)
                .gt(totalOrderAmountMin != null, "total_order_amount", totalOrderAmountMin)
                .lt(totalOrderAmountMax != null, "total_order_amount", totalOrderAmountMax)
                .gt(orderNumMin != null && orderNumMax != 0L, "order_num", orderNumMin)
                .lt(orderNumMax != null && orderNumMax != 0L, "order_num", orderNumMax)
                .eq(orderNumMax != null &&orderNum == 0L, "order_num", orderNum);

        userMapper.selectPage(userPage, queryWrapper);
        List<User> userList = userPage.getRecords();

        List<UserInfo> userInfoList =  UserConverter.INSTANCE.mapToVOList(userList);
        PageResponse<UserInfo> pageResponse = new PageResponse<>();
        pageResponse.setSuccess(true);
        pageResponse.setData(userInfoList);
        // 当前页数：第一页
        pageResponse.setCurrentPage((int) userPage.getCurrent());
        // 总记录数：100条
        pageResponse.setTotal((int) userPage.getTotal());
        // 总页数：5页
        pageResponse.setPageTotal((int) userPage.getPages());
        // 每页记录数: 每页10条
        pageResponse.setPageSize((int) userPage.getSize());
        return pageResponse;
    }

    @Override
    public User selectById(Long id) {
        return userMapper.selectById(id);
    }

    @Override
    public UserQueryResponse<Boolean> blockUser(UserBlockRequest request) {
        // 幂等 修改固定 天生幂等
        UserIdQueryCondition condition = (UserIdQueryCondition) request.getUserCondition();
        Long id = condition.getId();
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();
        wrapper.eq("id", id).set("state", UserStateEnum.BLOCK.name());

        int i = baseMapper.update(wrapper);
        if (i == 0) {
            throw new SystemException(ResponseCode.UPDATE_FAILED);
        }
        UserQueryResponse<Boolean> response = new UserQueryResponse<>();
        response.setSuccess(true);
        // 刷新session
        refreshUserInSession(condition.getId());
        return response;
    }

    @Override
    public NoResultResponse updateUserInfo(UserUpdateStatusRequest<UserUpdateStatusParam> request) {
        UserUpdateStatusParam param = request.getData();
        String state = param.getState();
        Long userId = param.getId();
        userServiceImpl.updateUserStatus(userId, state);
        NoResultResponse response = new NoResultResponse();
        response.setSuccess(true);
        // 刷新session
        refreshUserInSession(userId);
        return response;
    }

    @Override
    public String updatePhoto(MultipartFile file) {
        Long userId = Long.valueOf((String) StpKit.USER.getLoginId());
        String profilePhotoUrl = ossService.updatePhoto(file);
        if (!org.springframework.util.StringUtils.hasText(profilePhotoUrl)) {
            throw new OssException(OssErrorCode.PHOTO_UPLOAD_ERROR);
        }
        userServiceImpl.updateUserPhoto(userId, profilePhotoUrl);
        return profilePhotoUrl;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateUserPhoto(Long userId, String profilePhotoUrl) {
        User user = baseMapper.selectOne(new QueryWrapper<User>().eq("id", userId));
        baseMapper.update(user, new UpdateWrapper<User>().eq("id", userId).set("profile_photo_url", profilePhotoUrl));
        long l = userOperateStreamService.insertStream(user, UserOperateType.UPDATE_INFO);
        if (l == 0) {
            throw new BizException(ResponseCode.INSERT_FAILED);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateUserStatus(Long userId, String state) {
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();
        wrapper.eq("id", userId).set("state", state);
        boolean update = this.update(wrapper);
        if (!update) {
            throw new SystemException(ResponseCode.UPDATE_FAILED);
        }
        User user = baseMapper.selectOne(new QueryWrapper<User>().eq("id", userId));
        if (UserOperateType.BLOCK.name().equals(state)) {
            userOperateStreamService.insertStream(user, UserOperateType.BLOCK);
        } else {
            userOperateStreamService.insertStream(user, UserOperateType.UNBLOCK);
        }
    }

    public boolean nickNameExist(String nickName) {
        //如果布隆过滤器中存在，再进行数据库二次判断
        if (this.nickNameBloomFilter != null && this.nickNameBloomFilter.contains(nickName)) {
            return userMapper.selectOne(new QueryWrapper<User>().eq("nick_name", nickName)) != null;
        }
        return false;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.nickNameBloomFilter = redissonClient.getBloomFilter("nickName");
        if (nickNameBloomFilter != null && !nickNameBloomFilter.isExists()) {
            this.nickNameBloomFilter.tryInit(100000L, 0.01);
        }
    }

    private void refreshUserInSession(Long userId) {
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("id", userId));
        UserInfo userInfo = UserConvertor.INSTANCE.mapToVo(user);
        StpKit.USER.getSessionByLoginId(userId).set(userInfo.getId().toString(), userInfo);
    }
}
